package org.example.order.service;

import com.google.common.collect.Lists;
import lombok.SneakyThrows;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Random;

public class Test {
    private static final Long PING_INTERVAL_TIME = 2L;
    @SneakyThrows
    public static void main(String[] args) {
        List<Long> errors = Lists.newArrayList();
        for(int i = 0; i < 100; i++) {
            LocalDateTime now = LocalDateTime.now();
            List<LocalDateTime> randomClickEvent = randomClickEvent(now);
            List<LocalDateTime> uploadEvent = getUploadEvent(randomClickEvent);
            for (LocalDateTime click : randomClickEvent) {
                System.out.println("点击事件：" + click);
            }

            for (LocalDateTime upload : uploadEvent) {
                System.out.println("上报事件：" + upload);
            }

            long operationTime = calculateOperationTime(uploadEvent);
            long reallyOperationTime = calculateReallyOperationTime(randomClickEvent);
            long error = operationTime > reallyOperationTime ? operationTime - reallyOperationTime : reallyOperationTime - operationTime;
            System.out.println("实际操作时间：" + reallyOperationTime);
            System.out.println("计算得到的操作时间：" + operationTime);
            System.out.println("误差：" + error);
            writeFile(String.format("v%s.txt", PING_INTERVAL_TIME), String.format("%s----%s, 误差:%s", reallyOperationTime, operationTime, error));
            errors.add(error);
        }
        long avgError = errors.stream().reduce(0L, Long::sum) / errors.size();
        writeFile(String.format("v%s.txt", PING_INTERVAL_TIME), "平均误差: " + avgError);
    }

    private static long calculateReallyOperationTime(List<LocalDateTime> randomClickEvent) {
        long result = 0L;
        for (int i = 1; i < randomClickEvent.size(); i++) {
            long interval = toSeconds(randomClickEvent.get(i)) - toSeconds(randomClickEvent.get(i - 1));
            if (interval <= 30) {
                result += interval;
            }
        }
        return result;
    }

    private static long calculateOperationTime(List<LocalDateTime> uploadEvent) {
        long result = 0L;
        for (int i = 1; i < uploadEvent.size(); i++) {
            long interval = toSeconds(uploadEvent.get(i)) - toSeconds(uploadEvent.get(i - 1));
            if (interval < 30) {
                result += interval;
            }
        }
        return result;
    }

    private static List<LocalDateTime> randomClickEvent(LocalDateTime from) {
        List<LocalDateTime> clicks = Lists.newArrayList(from);
        for (int i = 0; i < 10; i++) {
            LocalDateTime nextClickTime = clicks.get(clicks.size() - 1).plusSeconds(getRandomNumber());
            clicks.add(nextClickTime);
        }
        return clicks;
    }

    private static List<LocalDateTime> getUploadEvent(List<LocalDateTime> randomClickEvent) {
        List<LocalDateTime> uploadEvent = Lists.newArrayList(randomClickEvent.get(0));
        long start = toSeconds(randomClickEvent.get(0));
        long end = toSeconds(randomClickEvent.get(randomClickEvent.size() - 1));
        while (start <= end + 30) {
            if (checkHaveClickEventInRangeTime(randomClickEvent, start, start + PING_INTERVAL_TIME)) {
                uploadEvent.add(toLocalDateTime(start + PING_INTERVAL_TIME));
            }
            start += PING_INTERVAL_TIME;
        }
        return uploadEvent;
    }

    private static boolean checkHaveClickEventInRangeTime(List<LocalDateTime> randomClickEvent, Long start, long end) {
        for (LocalDateTime time : randomClickEvent) {
            if (toSeconds(time) >= start && toSeconds(time) <= end) {
                return true;
            }
            if (toSeconds(time) > end) {
                return false;
            }
        }
        return false;
    }

    private static Integer getRandomNumber() {
        return new Random().nextInt(60);
    }

    private static Long toSeconds(LocalDateTime localDateTime) {
        return localDateTime.toInstant(ZoneOffset.ofHours(8)).toEpochMilli() / 1000L;
    }

    private static LocalDateTime toLocalDateTime(Long seconds) {
        return Instant.ofEpochMilli(seconds * 1000L).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
    }

    private static void writeFile(String fineName, String text) throws FileNotFoundException {
        PrintWriter printWriter = new PrintWriter(new FileOutputStream(fineName, true));
        printWriter.println(text);
        printWriter.flush();
        printWriter.close();
    }
}
